#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/i2c.h>
#include<linux/fs.h>
#include<linux/uaccess.h>
#include<linux/io.h>
#include<linux/device.h>
#include "pro.h"

struct cdev* cdev;
dev_t devno;
#if 0
unsigned int major = 0;
#else 
unsigned int major = 500;   //指定主设备号，三个驱动的主设备号不可以相同
#endif
unsigned int minor = 0;     //指定次设备号
struct class* cls;
struct device* dev;

struct i2c_client* client1;

//iic读取温湿度     参数为si7006中寄存器的地址
int iic_read_hum_tem(unsigned char reg)
{
    int ret;
    char r_buf[]={reg};
    short value;
    struct i2c_msg r_msg[]=
    {
        [0]={
            .addr=client1->addr,
            .flags=0,
            .len=1,
            .buf=r_buf,
        },
        [1]={
            .addr=client1->addr,
            .flags=1,
            .len=2,
            .buf=(char*)&value,
        },
    };
    ret=i2c_transfer(client1->adapter,r_msg,ARRAY_SIZE(r_msg));
    if(ret != ARRAY_SIZE(r_msg))
    {
        printk("消息发送失败\n");
        return ret;
    }
    return value;
}

int si7006_open(struct inode* inode,struct file* file)
{
    return 0;
}

int si7006_close(struct inode* inode,struct file* file)
{
    return 0;
}

/* si7006获取温湿度 并传输到用户空间
 * 用户空间转发给 QT 与 数码管驱动模块 */
long si7006_ioctl(struct file* file,unsigned int cmd,unsigned long arg)
{
    int tem,hum;
    int ret;
    switch(cmd)
    {
        case TEMP_L:
            //从si7006中的0xe5地址的寄存器中读取湿度
            hum=iic_read_hum_tem(0xe5);
            //将数据拷贝给应用程序
            ret=copy_to_user((void *)arg,&hum,sizeof(int));
            if(ret)
            {
                printk("想用户层拷贝失败\n");
                return -EIO;
            }
            break;
        case HUM_L:
            //从si7006中的0xe3地址的寄存器中读取温度
            tem=iic_read_hum_tem(0xe3);
            //将数据拷贝给应用程序
            ret=copy_to_user((void *)arg,&tem,sizeof(int));
            if(ret)
            {
                printk("想用户层拷贝失败\n");
                return -EIO;
            }
            break;
    }
    return 0;
}

//设置操作方法
struct file_operations fops={
    .open=si7006_open,
    .unlocked_ioctl=si7006_ioctl,
    .release=si7006_close,
};

//platform连接函数
int i2c_probe(struct i2c_client* client,const struct i2c_device_id* id)
{
    int ret;
    client1=client;
    // 1.分配对象
    cdev = cdev_alloc();
    if (NULL == cdev)
    {
        printk("分配对象空间失败\n");
        ret = -ENOMEM;
        goto ERR1;
    }
    // printk("分配对象空间成功\n");
    // 2.初始化驱动对象
    cdev_init(cdev, &fops);
    if (0 == major)
    {
        ret = alloc_chrdev_region(&devno, minor, 3, "si7006");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto ERR2;
        }
        major = MAJOR(devno);
        minor = MINOR(devno);
    }
    else if (major > 0)
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "si7006");
        if (ret)
        {
            printk("静态申请设备号失败\n");
            goto ERR2;
        }
    }
    // 3.注册对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("驱动对象注册进内核失败\n");
        goto ERR3;
    }
    // printk("驱动对象注册进内核成功\n");
    cls = class_create(THIS_MODULE, "si7006");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        goto ERR4;
    }
    // printk("向上提交目录成功\n");

    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "si7006");
    if (IS_ERR(dev))
    {
        printk("向上提交结点信息失败\n");
        goto ERR5;
    }
    return 0;
ERR5:
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), 3);
ERR2:
    kfree(cdev);
ERR1:
    return ret;
    return 0;
}

//卸载函数 释放申请的资源
int i2c_remove(struct i2c_client* client)
{
     device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    cdev_del(cdev);
    unregister_chrdev_region(MKDEV(major, minor), 3);
    kfree(cdev);
    return 0;
}

struct of_device_id oftable[]=
{
    {.compatible="hqyj,si7006"},
    {},
};

struct i2c_driver i2c_drv=
{
    .probe=i2c_probe,
    .remove=i2c_remove,
    .driver=
    {
        .name="si7006",
        .of_match_table=oftable,
    },
};

module_i2c_driver(i2c_drv);
MODULE_LICENSE("GPL");